package com.dou.you.che.service.impl.sys;

import com.dou.you.che.service.sys.SysScheduleJobServiceI;
import com.dou.you.che.comm.utils.SpringContextUtils;
import com.dou.you.che.comm.utils.constant.Constant;
import com.dou.you.che.comm.utils.exception.WebException;
import com.dou.you.che.comm.utils.response.RspData;
import com.dou.you.che.core.entity.sys.SysScheduleJob;
import com.dou.you.che.core.mapper.sys.SysScheduleJobDao;
import com.dou.you.che.service.schedule.ScheduleJob;
import org.apache.log4j.Logger;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 定时任务
 * @Author 黄国刚【1058118328@qq.com】
 */
@Service
@Transactional(readOnly = true)
public class SysScheduleJobService implements SysScheduleJobServiceI {

    private static final Logger logger = Logger.getLogger(SysScheduleJobService.class);

    @Autowired
    private Scheduler scheduler;
    @Autowired
    private SysScheduleJobDao schedulerJobDao;

    private final static String JOB_NAME = "XEE_TASK_";

    /**
     * 项目启动时，初始化定时器
     */
    @PostConstruct
    public void init() {
        List<SysScheduleJob> scheduleJobList = schedulerJobDao.queryList(new HashMap<String, Object>());
        for (SysScheduleJob scheduleJob : scheduleJobList) {
            CronTrigger cronTrigger = getCronTrigger(scheduler, scheduleJob.getJobId());
            //如果不存在，则创建
            if (cronTrigger == null) {
                createScheduleJob(scheduler, scheduleJob);
            } else {
                updateScheduleJob(scheduler, scheduleJob);
            }
        }
    }

    @Override
    public SysScheduleJob queryObject(Long jobId) {
        return schedulerJobDao.queryObject(jobId);
    }

    @Override
    public List<SysScheduleJob> queryList(Map<String, Object> map) {
        return schedulerJobDao.queryList(map);
    }

    @Override
    public int queryTotal(Map<String, Object> map) {
        return schedulerJobDao.queryTotal(map);
    }

    @Override
    @Transactional
    public RspData save(SysScheduleJob scheduleJob) {
        Object o = null;
        try {
            o = SpringContextUtils.getBean(scheduleJob.getBeanName());
        } catch (Exception e) {
            logger.error("任务类没找到");
            return RspData.error("任务类没找到");
        }
        scheduleJob.setCreateTime(new Date());
        scheduleJob.setStatus(Constant.ScheduleStatus.NORMAL.getValue());
        schedulerJobDao.save(scheduleJob);

        createScheduleJob(scheduler, scheduleJob);
        return RspData.ok();
    }

    @Override
    @Transactional
    public RspData update(SysScheduleJob scheduleJob) {
        Object o = null;
        try {
            o = SpringContextUtils.getBean(scheduleJob.getBeanName());
        } catch (Exception e) {
            logger.error("任务类没找到");
            return RspData.error("任务类没找到");
        }
        List<String> list = new ArrayList<>();

        Class c = o.getClass();
        Method[] methods = c.getDeclaredMethods();

        for (int i = 0; i < methods.length; i++) {
            list.add(methods[i].getName());
        }
        if (!list.contains(scheduleJob.getMethodName())) {
            logger.error("任务类没找到该方法：" + scheduleJob.getMethodName());
            return RspData.error("任务类没找到该方法：" + scheduleJob.getMethodName());
        }

        updateScheduleJob(scheduler, scheduleJob);

        schedulerJobDao.update(scheduleJob);
        return RspData.ok();
    }

    @Override
    @Transactional
    public void deleteBatch(Long[] jobIds) {
        for (Long jobId : jobIds) {
            deleteScheduleJob(scheduler, jobId);
        }

        //删除数据
        schedulerJobDao.deleteBatch(jobIds);
    }

    @Override
    public int updateBatch(Long[] jobIds, int status) {
        Map<String, Object> map = new HashMap<>();
        map.put("list", jobIds);
        map.put("status", status);
        return schedulerJobDao.updateBatch(map);
    }

    @Override
    @Transactional
    public void run(Long[] jobIds) {
        for (Long jobId : jobIds) {
            run(scheduler, queryObject(jobId));
        }
    }

    @Override
    @Transactional
    public void pause(Long[] jobIds) {
        for (Long jobId : jobIds) {
            pauseJob(scheduler, jobId);
        }

        updateBatch(jobIds, Constant.ScheduleStatus.PAUSE.getValue());
    }

    @Override
    @Transactional
    public void resume(Long[] jobIds) {
        for (Long jobId : jobIds) {
            resumeJob(scheduler, jobId);
        }

        updateBatch(jobIds, Constant.ScheduleStatus.NORMAL.getValue());
    }

    /**
     * 获取触发器key
     *
     * @param jobId jobId
     * @return
     */
    public static TriggerKey getTriggerKey(Long jobId) {
        return TriggerKey.triggerKey(JOB_NAME + jobId);
    }

    /**
     * 获取jobKey
     *
     * @param jobId jobId
     * @return
     */
    public static JobKey getJobKey(Long jobId) {
        return JobKey.jobKey(JOB_NAME + jobId);
    }

    /**
     * 获取表达式触发器
     *
     * @param scheduler scheduler
     * @param jobId jobId
     * @return CronTrigger
     */
    public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) {
        try {
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            throw new WebException("获取定时任务CronTrigger出现异常", e);
        }
    }

    /**
     * 创建定时任务
     *
     * @param scheduler scheduler
     * @param scheduleJob scheduleJob
     */
    public static void createScheduleJob(Scheduler scheduler, SysScheduleJob scheduleJob) {
        try {
            //构建job信息
            JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getJobId())).build();

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getJobId())).withSchedule(scheduleBuilder).build();

            //放入参数，运行时的方法可以获取
            jobDetail.getJobDataMap().put(SysScheduleJob.JOB_PARAM_KEY, scheduleJob);

            scheduler.scheduleJob(jobDetail, trigger);

            //暂停任务
            if (scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()) {
                pauseJob(scheduler, scheduleJob.getJobId());
            }
        } catch (SchedulerException e) {
            throw new WebException("创建定时任务失败", e);
        }
    }

    /**
     * 更新定时任务
     *
     * @param scheduler scheduler
     * @param scheduleJob scheduleJob
     */
    public static void updateScheduleJob(Scheduler scheduler, SysScheduleJob scheduleJob) {
        try {
            TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId());

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getJobId());

            //按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

            //参数
            trigger.getJobDataMap().put(SysScheduleJob.JOB_PARAM_KEY, scheduleJob);

            scheduler.rescheduleJob(triggerKey, trigger);

            //暂停任务
            if (scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()) {
                pauseJob(scheduler, scheduleJob.getJobId());
            }

        } catch (SchedulerException e) {
            throw new WebException("更新定时任务失败", e);
        }
    }

    /**
     * 立即执行任务
     *
     * @param scheduler scheduler
     * @param scheduleJob scheduleJob
     */
    public static void run(Scheduler scheduler, SysScheduleJob scheduleJob) {
        try {
            //参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(SysScheduleJob.JOB_PARAM_KEY, scheduleJob);

            scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
        } catch (SchedulerException e) {
            throw new WebException("立即执行定时任务失败", e);
        }
    }

    /**
     * 暂停任务
     *
     * @param scheduler scheduler
     * @param jobId jobId
     */
    public static void pauseJob(Scheduler scheduler, Long jobId) {
        try {
            scheduler.pauseJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new WebException("暂停定时任务失败", e);
        }
    }

    /**
     * 恢复任务
     *
     * @param scheduler scheduler
     * @param jobId jobId
     */
    public static void resumeJob(Scheduler scheduler, Long jobId) {
        try {
            scheduler.resumeJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new WebException("暂停定时任务失败", e);
        }
    }

    /**
     * 删除定时任务
     *
     * @param scheduler scheduler
     * @param jobId jobId
     */
    public static void deleteScheduleJob(Scheduler scheduler, Long jobId) {
        try {
            scheduler.deleteJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new WebException("删除定时任务失败", e);
        }
    }
}
